package com.tmobile.phonebook;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.tmobile.phonebook.exceptions.BusinessException;
import com.tmobile.phonebook.exceptions.ErrorCodes;

public class PhoneBook 
{
	// setting the concurrency level to 1 - since there is only 1 thread making the update
	// concurrencyLevel can be adjusted to allow n number of threads
	// and the other thread is a read-only display thread hence using a ConcurrentHashMap 
	// because the iterators don't fail-fast with ConcurrentModificationException 
	private static final ConcurrentHashMap<Long, Subscriber> directory = 
		new ConcurrentHashMap<Long, Subscriber>(16, 0.75f, 1);
	
	protected void addSubscriber(long phoneNumber, String name, float balanceInCents,float decrementRate) throws BusinessException
	{
		if (isValidPhoneNumber(phoneNumber))
		{
			if (null != directory.putIfAbsent(phoneNumber, new Subscriber(phoneNumber, name, balanceInCents, decrementRate)))
			{
				throw new BusinessException(ErrorCodes.SUBSCRIBER_ALREADY_EXISTS);
			}
		}
		else 
		{
			throw new BusinessException(ErrorCodes.INVALID_SUBSCRIBER_DEFINITION);
		}
		
		System.out.println("Subscriber created and saved!");
	}
	
	//retrieve existing Subscriber from directory
	protected Subscriber getSubscriber(long phoneNumber) throws BusinessException
	{
		
		Subscriber s = directory.get(phoneNumber);
		
		if (s != null) 
		{
			return s;
		}
		else 
		{
			throw new BusinessException(ErrorCodes.SUBSCRIBER_DOES_NOT_EXIST);
		}
	}
	
	//remove subscriber from directory
	protected Subscriber removeSubscriber (long phoneNumber) throws BusinessException
	{
		Subscriber s = directory.remove(phoneNumber);
		
		if (s != null) 
		{
			return s;
		}
		else 
		{
			throw new BusinessException(ErrorCodes.SUBSCRIBER_DOES_NOT_EXIST);
		}
	}
	
	//update balance of the Subscriber balanceIncrement is in cents
	protected void updateBalance(long phoneNumber, float balanceIncrement) throws BusinessException
	{
		Subscriber old = this.getSubscriber(phoneNumber);
		Subscriber newSubscriber = new Subscriber(old); 
			
		float newBalance = old.getBalanceInCents() + balanceIncrement;
		boolean newBalanceValid = (newBalance >= 0.0f) ? ( newBalance <= 1000.0f ) ? true:false:false;  
	
		if (newBalanceValid)
		{
			newSubscriber.setBalanceInCents(newBalance);
			if (!directory.replace(phoneNumber, old, newSubscriber))
			{
				throw new BusinessException(ErrorCodes.SUBSCRIBER_DOES_NOT_EXIST);
			}
		}
		else
		{
			throw new BusinessException(ErrorCodes.BALANCE_OUT_OF_RANGE);
		}
	}
	
	protected String getRemainingBalanceAndMinutes(long phoneNumber) throws BusinessException
	{
		Subscriber s = 	getSubscriber(phoneNumber);
		float balance = s.getBalanceInCents();
		float rate = s.getDecrementRate();

		return "Balance(cents):" + balance + " RemainingMinutes:" + Math.floor((balance/rate));   
	}
	
	private boolean isValidPhoneNumber(long n)
	{
		return (n > 999999999l) ? ( (n < 10000000000l)? true:false):false;
	} 

	//Iterate and display phone book
	public void displayPhoneBook()
	{
		System.out.println("\n-------------------------------------------------");
		System.out.println("\tNumber" + "\t\t" + "Name" );
		System.out.println("-------------------------------------------------");
		
		for (Map.Entry<Long, Subscriber> e:directory.entrySet())
			System.out.println("\t" + e.getKey() + "\t" + e.getValue().getName());	
		
	}
	
}
